package cn.xuqiudong.rpc.cluster;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import cn.xuqiudong.rpc.cluster.balance.LoadBalance;
import cn.xuqiudong.rpc.common.model.Invoker;
import cn.xuqiudong.rpc.extension.XqdExtensionFactory;
import cn.xuqiudong.rpc.common.model.XqdUrl;
import cn.xuqiudong.rpc.registry.NotifyListener;
import cn.xuqiudong.rpc.registry.RegistrySingle;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 描述: 集群门面入口，暂时比较简陋，只引入负载均衡（以及服务订阅） 后续可考虑引入熔断和消费降级等
 * @author Vic.xu
 * @date 2022-03-04 10:47
 */
public class ClusterFacade {

    private static Logger logger = LoggerFactory.getLogger(ClusterFacade.class);

    private static LoadBalance loadBalance = XqdExtensionFactory.defaultExtendFactory().getAdaptiveXqdExtension(LoadBalance.class);


    /**
     * 缓存在本地的服务端地址, 订阅注册中心的地址变化
     */
    private static Map<String, List<XqdUrl>> CACHED_URL_MAP = new ConcurrentHashMap<>();

    /**
     * 从注册中心或缓存获取URL 并进行负载均衡
     * @param invoker
     * @return
     */
    public static  XqdUrl fetchUrlLoadBalance(Invoker invoker){
        String interfaceName = invoker.getInterfaceName();
        List<XqdUrl> urls = CACHED_URL_MAP.get(interfaceName);
        if (CollectionUtils.isEmpty(urls)) {
            XqdUrl url = new XqdUrl();
            url.setInterfaceName(interfaceName);
            //从单例的注册中心中查询服务列表
            urls = RegistrySingle.getRegistry().lookup(url);
            CACHED_URL_MAP.put(interfaceName, urls);
            //订阅注册中心中url的变化
            subscribe(url);
        }

        return loadBalance.select(urls, invoker);
    }

    /**
     * 订阅注册中心的url变化
     * @param url
     */
    private static void subscribe(XqdUrl url){
        RegistrySingle.getRegistry().subscribe(url, new NotifyListener() {
            @Override
            public void notify(List<XqdUrl> urls) {
                logger.info("收到订阅的{}服务地址全量推送{}条, 更新本地缓存",url.getInterfaceName(), urls.size());
                CACHED_URL_MAP.put(url.getInterfaceName(), urls);
            }
        });
    }
}
